package plp.orientadaObjetos2.declaracao.variavel;

import plp.expressions2.memory.VariavelJaDeclaradaException;
import plp.expressions2.memory.VariavelNaoDeclaradaException;
import plp.orientadaObjetos1.comando.ChamadaProcedimento;
import plp.orientadaObjetos1.comando.New;
import plp.orientadaObjetos1.comando.Procedimento;
import plp.orientadaObjetos1.declaracao.variavel.DecVariavelObjeto;
import plp.orientadaObjetos1.declaracao.variavel.SimplesDecVariavel;
import plp.orientadaObjetos1.excecao.declaracao.ClasseJaDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ClasseNaoDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ObjetoJaDeclaradoException;
import plp.orientadaObjetos1.excecao.declaracao.ObjetoNaoDeclaradoException;
import plp.orientadaObjetos1.excecao.declaracao.ProcedimentoNaoDeclaradoException;
import plp.orientadaObjetos1.expressao.ListaExpressao;
import plp.orientadaObjetos1.expressao.leftExpression.Id;
import plp.orientadaObjetos1.expressao.valor.ValorNull;
import plp.orientadaObjetos1.memoria.AmbienteCompilacaoOO1;
import plp.orientadaObjetos1.memoria.AmbienteExecucaoOO1;
import plp.orientadaObjetos1.memoria.ContextoExecucaoOO1;
import plp.orientadaObjetos1.util.Tipo;
import plp.orientadaObjetos1.util.TipoClasse;
import plp.orientadaObjetos2.comando.NewOO2Padrao;
import plp.orientadaObjetos2.comando.NewOO2Reflection;
import plp.orientadaObjetos2.memoria.AmbienteExecucaoOO2;
import plp.orientadaObjetos2.memoria.ContextoCompilacaoOO2;
import plp.orientadaObjetos2.memoria.DefClasseOO2;
import plp.orientadaObjetos2.reflection.ReflectionUtil;
import plp.orientadaObjetos2.util.SuperClasseUtil;

public class DecVariavelObjetoOO2 extends DecVariavelObjeto {

	private ListaExpressao parametrosReais;

	public DecVariavelObjetoOO2(Tipo tipo, Id objeto, Id classe,
			ListaExpressao parametrosReais) {
		super(tipo, objeto, classe);
		this.parametrosReais = parametrosReais;
	}

	public AmbienteExecucaoOO1 elabora(AmbienteExecucaoOO1 ambiente)
			throws VariavelJaDeclaradaException, VariavelNaoDeclaradaException,
			ObjetoNaoDeclaradoException, ObjetoJaDeclaradoException,
			ClasseNaoDeclaradaException, ClasseJaDeclaradaException {
		
		//Esse if else � uma solu��o pra o problema de uma classe ter campos que s�o objetos de outras classes
		if ((ambiente.getClass().equals(ContextoExecucaoOO1.class)) && this.getClass().equals(DecVariavelObjetoOO2.class)){
			AmbienteExecucaoOO1 aux = new SimplesDecVariavel(getTipo(), getObjeto(), new ValorNull()).elabora(ambiente);
			aux = new New(getObjeto(), getClasse()).executar(ambiente);
			return aux;
		} else {
			AmbienteExecucaoOO2 aux = (AmbienteExecucaoOO2) new SimplesDecVariavel(
					getTipo(), getObjeto(), new ValorNull()).elabora(ambiente);
			try {
				if (ReflectionUtil.isMetaClasse(getClasse())) {
					aux = new NewOO2Reflection(getObjeto(), getClasse(), parametrosReais).executar((AmbienteExecucaoOO2)aux);
				} else {
					aux = new NewOO2Padrao(getObjeto(), getClasse(), parametrosReais).executar(aux);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return aux;
		}
	}

	public boolean checaTipo(AmbienteCompilacaoOO1 ambiente) throws VariavelJaDeclaradaException, VariavelNaoDeclaradaException, ClasseJaDeclaradaException, ClasseNaoDeclaradaException {
		ContextoCompilacaoOO2 ambiente2 = (ContextoCompilacaoOO2)ambiente;
		//checatipo da superclasse com verificacao de polimorfismo. deixa passar se a variavel for do tipo da superclasse e o objeto da subclasse
		boolean booleanSuper = false;
		TipoClasse tpClasse = new TipoClasse(getClasse());//getClasse � tipo do objeto, getTipo eh o tipo da variavel
		if (tpClasse.eValido(ambiente2) && getTipo().eValido(ambiente2)) {
			booleanSuper = SuperClasseUtil.isClasseOrSuperclasse(ambiente2, getTipo(), tpClasse);
			ambiente2.map(getObjeto(), tpClasse);
		}
		
		//salva em um hashmap o mapeamento entre as vari�veis de objeto e seus tipos
		ambiente2.mapTipoVariavelObjetoOO2(getObjeto(), getTipo());
		
		//coisas da subclasse
		DefClasseOO2 defClasseObjeto = (DefClasseOO2) ambiente2.getDefClasse(getObjeto().getTipo(ambiente2).getTipo());
		
		Procedimento construtor = defClasseObjeto.getConstrutor().getProcedimento();//construtor
		//ok, deixa no checa tipo ele pegar os dados do tipo do objeto ja que aqui ele lida com o construtor, e o construtor tem q ser o do proprio objeto mesmo
		
		boolean resposta = false;
		
		if (construtor != null) {
			try {
				ambiente2.incrementa();
				resposta = new ChamadaProcedimento(construtor, parametrosReais).checaTipo(ambiente2);
				ambiente2.restaura();
			} catch (ProcedimentoNaoDeclaradoException e) {
				throw new RuntimeException("Construtor n�o declarado.");
			}
		}
		
		return booleanSuper && resposta;
	}
}
